'use strict';

const { Controller } = require('egg');
const path = require('path');
const fs = require('fs');

// 存放文件的绝对目录
const FILE_PATH_UPLOAD = path.resolve(__dirname, '../../uploadFiles');

class HomeController extends Controller {
	// 检查文件接口
	async checkFile() {
		const { ctx } = this
		// 获取文件唯一hash
		const { hash } = ctx.request.body
		// 获取已经上传过文件列表 
		const uploadFilesList = (await fs.promises.readdir(FILE_PATH_UPLOAD)).map(pathname => path.join(FILE_PATH_UPLOAD, pathname))
		// 查看一下是否存在一个目录或者文件和我们传递过来的hash一样
		const exist = uploadFilesList.find(pathname => {
			// 获取文件名字
			const { name } = path.parse(pathname)
			// 查看hash是否存在name
			return name === hash
		})
		// 是否存在该目录
		if (exist) {
			// 存在
			// 分辨是文件还是文件夹
			// 返回值true 是文件夹
			// 返回值false 是文件
			const isDir = (await fs.promises.stat(exist)).isDirectory();
			// 存在文件夹
			if (isDir) {
				ctx.body = {
					code: 202,
					message: '文件已经上传部分， 请继续上传',
					data: {
						uploaded: true,
						existsChunks: await fs.promises.readdir(exist)
					}
				}
				// 存在文件
			} else {
				ctx.body = {
					code: 201,
					message: '文件已经上传, 秒传成功！',
					data: {
						uploaded: true,
					}
				}
			}

		} else {
			// 不存在
			ctx.body = {
				code: 200,
				message: '文件未上传，请开始上传',
				data: {
					uploaded: false,
				}
			}
		}
	}
	// 上传文件接口
	async upload() {
		const { ctx } = this;
		// 获取上传信息
		const file = ctx.request.files[0];
		//文件名称
		const filename = file.filename
		// 文件路径
		const filepath = file.filepath
		// 获取hash
		const hash = filename.split('-')[0]

		try {
			// 获取目录
			const exist = fs.existsSync(path.join(FILE_PATH_UPLOAD, hash));
			// 不存在创建文件夹
			if (!exist) await fs.promises.mkdir(path.join(FILE_PATH_UPLOAD, hash))
			// 复制文件
			await fs.promises.copyFile(filepath, path.join(FILE_PATH_UPLOAD, hash, filename))
		} finally {
			// 操作完成删除临时文件
			ctx.cleanupRequestFiles();
		}

		ctx.body = {
			code: 200,
			data: null,
			message: `${filename} 上传成功`
		};
	}
	// 合并文件 
	async mergeFile() {
		const { ctx } = this;
		// 获取上传的信息
		const { hash, extname, originName } = ctx.request.body;
		// 获取hash所在目录
		const hashDir = path.join(FILE_PATH_UPLOAD, hash);
		// 验证目录是否存在
		if (fs.existsSync(hashDir)) {
			// 获取合并目录
			const sourceFiles = (await fs.promises.readdir(hashDir)).sort((a, b) => {
				const aIndex = path.parse(a).name.split('-')[1]
				const bIndex = path.parse(b).name.split('-')[1];
				// 获取正确的切片位置
				return +aIndex - +bIndex
			}).map(v => path.join(hashDir, v))

			// 获取目标文件
			const targetFile = path.join(FILE_PATH_UPLOAD, hash + extname)
			// 开始合并
			await this.streamMergeFile(sourceFiles, targetFile, hash);
			const info = await fs.promises.stat(targetFile)
			let result;
			// 是否上传阿里云
			if (info.size < 2 * 1024 ** 2) {
				try {
					result = await ctx.oss.put(originName, path.normalize(targetFile))
					console.log(result.url);
				} catch (e) {
					console.log('上传阿里云失败');
					console.log(e)
				}
			}

			// 写入数据库
			try {
				await this.addUploadRecord({
					user_id: ctx.state.userinfo.userInfo.id,
					user_name: ctx.state.userinfo.userInfo.user_name,
					size: info.size,
					path: targetFile,
					oss: result ? result.url : '',
					name: hash,
					extname,
					filename: hash + extname,
					originName,
					status: 1,
					upload_ip: ctx.request.ip
				})
			} catch (e) {
				console.log('数据库添加失败')
				console.log(e)
			}
			// 监测数据库写入

			ctx.body = {
				code: 200,
				message: '文件合并成功'
			}

		} else {
			ctx.body = {
				code: 400,
				message: '请求合并的文件不存在'
			}
		}
	}
	// 多文件合并
	async streamMergeFile(sourceFiles, targetFile, hash) {
		// 创建写入流
		const write = fs.createWriteStream(targetFile);
		// 开始递归写入文件
		return await this.readFileAndWrireFile(sourceFiles, write, hash)
	}

	// 流文件处理
	readFileAndWrireFile(sourceFiles = [], write, hash) {
		return new Promise(async (resolve) => {
			if (sourceFiles.length === 0) {
				// 所有切片写入完毕
				// 删除临时文件件
				await fs.promises.rmdir(path.join(FILE_PATH_UPLOAD, hash))
				// 结束
				write.end('结束');
				resolve()
				return
			}
			// 获取源文件文件
			const source = sourceFiles.shift();
			// 创建读取流
			const read = fs.createReadStream(source);
			// 管道流写入
			read.pipe(write, { end: false });
			// 自定义 写入完成
			read.on('end', async () => {
				// 删除源文件
				await fs.promises.unlink(source)
				// 继续写入下一个切片
				this.readFileAndWrireFile(sourceFiles, write, hash);
				resolve()
			})
		})

	}
	// 添加数据库记录
	async addUploadRecord({ user_id, user_name, size, path, oss, name, extname, filename, originName, status, upload_ip }) {
		// 数据 
		const data = {
			user_id,
			user_name,
			size,
			path,
			oss,
			name,
			extname,
			filename,
			originName,
			date: new Date(),
			status,
			upload_ip,
		}
		// 
		const result = this.ctx.service.upload.addUploadRecord(data);
		return result
	}
}

module.exports = HomeController;
